using UnityEngine;
using System.Collections;

public class csHomingMissleCtrl : csBulletAttributes 
{
#region Definition
	protected enum STATE_TYPE
	{
		None = 0,		//None
		Dropping,		//Dropping
		Launch,			//Launch
		Flying			//Fly
	}
	
	protected delegate void STATUS_FUNC();
	protected const float DEFAULT_DROP_DISTANCE = 10.0f;
#endregion
	
#region InputData
	public GameObject 	TargetPrefab;						//Target GameObject Prefab
	public string		TargetTagName;						//Target tag name. If the target is not a prefab object, please use this parameter to set the target.
	public float		FlySpeed			= 5.0f;			//Moving Speed. 		Unit: Pixel per Frame.
	public float		DroppingSpeed		= 30.0f;		//Dropping speed.		Unit: Frame.
															//	Need to sync with csHomingMissleLauncherCtrl::ShootSpeed.
	public float		DroppingOffset		= 1.0f;			//The Dropping Offset.	Unit: Pixel per Frame.
	public Vector3		MoveDirect			= Vector3.up;	//The direction when moving.
	public GameObject	ExplosionFXPrefab;					//Explosion FX Prefab.
	public string		DestroyTag;							//For Self destroy
#endregion
	
#region DataMember
	protected System.Collections.Generic.Dictionary<STATE_TYPE, STATUS_FUNC> mStateFuncList = new System.Collections.Generic.Dictionary<STATE_TYPE, STATUS_FUNC>();
	protected STATE_TYPE 	mCurStatus 		= STATE_TYPE.None;	
	
	private Transform 		mTransform;						//The transform of gameobject. The Official manual said this will optimize performance.
	protected GameObject	mLockedTarget = null;			//Locked target
	protected Vector3		mPrePos;						//Previous position  - for self destroy using
	protected float			mDropOffset;					//Offset when dropping
	protected Vector3		mDepositePos;					//The target deposited position.
	protected Vector3		mDroppingDirect;				//The dropping direction.
#endregion
	
#region MemberFunc
//NOTE:The offset is according to the Global Coordinate.	
	public void EnableShoot(Vector3 TargetPos, Vector3 DroppingDirect)
	{
		this.mDepositePos 		= TargetPos;
		this.mDroppingDirect	= DroppingDirect;
		this.mCurStatus 		= csHomingMissleCtrl.STATE_TYPE.Dropping;
		this.mDropOffset		= Vector3.Distance(this.mTransform.position, this.mDepositePos) / this.DroppingSpeed;
		
	//Check the Drop offset
		if( 1.0e-3 >= this.mDropOffset && this.mDropOffset >= 0 )
			this.mDropOffset = 1.0f;
	}
	
	protected void Move()
	{
		if(!this.mLockedTarget || !this.mLockedTarget.collider.enabled || !this.mLockedTarget.active)
		{
		//Because of the missle is moving with the FORWARD direction(Z+), just moving ahead here.
			this.mTransform.Translate(this.MoveDirect * this.FlySpeed);			
		}else{				
		//Record previous position
			this.mPrePos = this.mTransform.position;
			
		//Homing Move
		//	Vector3.MoveTowards() uses the Vector3.forward to be head of movement. If you want to use this function, remember to let the model head in Z+ direct.					
			this.mTransform.position = Vector3.MoveTowards(this.mTransform.position, this.mLockedTarget.transform.position, this.FlySpeed);
			
		//Face to target
		//	Transform.LookAt() uses the Vector3.forward to be head of movement. If you want to use this function, remember to let the model head in Z+ direct.		
			this.mTransform.LookAt(this.mLockedTarget.transform.position, Vector3.back);//this.mLockedTarget.transform);
		}			
	}
	
	protected GameObject FindCloselyTarget()
	{
	//Find Target	
		GameObject[] Targets = GameObject.FindGameObjectsWithTag(this.TargetTagName);
		if(Targets.Length == 0)
			return null;
		
		GameObject TheCloselyTarget = Targets[0];
		float TheCloselyDistance = Vector3.Distance(TheCloselyTarget.transform.position, this.transform.position);
		float Distance = 0.0f;
		
		foreach (GameObject target in Targets)
		{
			Distance = Vector3.Distance(this.transform.position, target.transform.position);
			if(Distance >= TheCloselyDistance)
				continue;
			
			TheCloselyTarget 	= target;
			TheCloselyDistance	= Distance;
		}
				
		return TheCloselyTarget.active ? TheCloselyTarget : null;		
	}
#endregion
	
#region StateFunc
	protected void State_Dropping()
	{
	//Move to the deposited position
		if( Vector3.Distance(this.mTransform.position, this.mDepositePos) > this.mDropOffset ) {
			this.mTransform.position += this.mDropOffset * this.mDroppingDirect;		
			return;
		}
		
		this.mTransform.position = this.mDepositePos;
		
	//Let Missle fly
		this.mCurStatus = csHomingMissleCtrl.STATE_TYPE.Launch;
	}
	
	protected void State_Launch()
	{
	//Enable Missle fly			
		this.MoveDirect 	= Vector3.forward;
		this.mLockedTarget 	= this.FindCloselyTarget();	

		this.mCurStatus 	= csHomingMissleCtrl.STATE_TYPE.Flying;
	}	
	
	protected void State_Flying()
	{
		this.Move();
	}
#endregion
	
#region EventFunc
	protected void OnDestroy()
	{
	//Just destroy self
		Object.Destroy(this.gameObject);
	}
#endregion
	
#region SystemFunc
	void Awake()
	{
	//For Optimization
		this.mTransform = this.transform;
	}
	
	// Use this for initialization
	void Start () 
	{		
	//Build State Function List
		this.mStateFuncList[STATE_TYPE.None]		= null;
		this.mStateFuncList[STATE_TYPE.Dropping]	= new csHomingMissleCtrl.STATUS_FUNC(this.State_Dropping);
		this.mStateFuncList[STATE_TYPE.Launch]		= new csHomingMissleCtrl.STATUS_FUNC(this.State_Launch);
		this.mStateFuncList[STATE_TYPE.Flying]		= new csHomingMissleCtrl.STATUS_FUNC(this.State_Flying);		
	}
	
	// Update is called once per frame
	void Update () 
	{		
	//Self check
		if(this.mTransform.position.z > 1.0e-4)
			this.OnDestroy();
		
	//Status Update
		if(this.mStateFuncList[this.mCurStatus] != null)
			this.mStateFuncList[this.mCurStatus]();		
	}
	
	void OnTriggerEnter(Collider ColiObj)
	{
		if(ColiObj == null)
			return;
		
	//Check the collider object
		if(this.TargetPrefab)
		{
			if(ColiObj.tag == this.TargetPrefab.tag)
				this.OnDestroy();
		}else{
			if(this.TargetTagName.Length > 0)
			{
				if(ColiObj.tag == this.TargetTagName)
					this.OnDestroy();
			}
		}	//End of if(this.TargetPrefab)
		
		if(ColiObj.tag == this.DestroyTag)
			this.OnDestroy();
	}
#endregion	
	
#region PropertiesOP
	public void SetDroppingSpeed(float DroppingSpeed) { this.DroppingSpeed = DroppingSpeed; }		//Unit: Frame
#endregion
}
